export const getType = (value: unknown) => {
    if (null === value) {
        return 'null'
    }
    const type = typeof value
    if ('undefined' === type || 'string' === type) {
        return type
    }
    const typeString = Object.prototype.toString.call(value)
    switch (typeString) {
        case '[object Array]':
            return 'array'
        case '[object Date]':
            return 'date'
        case '[object Boolean]':
            return 'boolean'
        case '[object Number]':
            return 'number'
        case '[object Function]':
            return 'function'
        case '[object RegExp]':
            return 'regexp'
        case '[object Object]':
            return 'object'
        case '[object Symbol]':
            return 'symbol'
        case '[object Map]':
            return 'map'
        case '[object Set]':
            return 'set'
        default:
            return 'unknow'
    }
}

export const isMap = (val: unknown): val is Map<unknown, unknown> => getType(val) === 'map'
export const isSet = (val: unknown): val is Set<unknown> => getType(val) === 'set'

export const isRegexp = (val: unknown): val is RegExp => getType(val) === 'regexp'
export const { isArray } = Array
export const isDate = (val: unknown): val is Date => val instanceof Date
export function isUndefined(val: unknown): val is undefined {
    return val === undefined
}
export const isSymbol = (value: unknown) => {
    const type = typeof value
    return type == 'symbol' || (type === 'object' && value != null && getType(value) === 'symbol')
}

export const isObject = (val: unknown): val is { [key: string]: unknown } => {
    return getType(val) === 'object'
}

export function isEmptyObject(obj: unknown): boolean {
    return isObject(obj) && Object.keys(obj).length === 0
}
export const isBoolean = (val: unknown): val is boolean => getType(val) === 'boolean'
// 原始数据类型 Types: number , string , boolean , symbol, bigint, undefined, null
export const isPrimitive = (value: unknown): boolean => {
    return (
        value === undefined ||
        value === null ||
        (typeof value !== 'object' && typeof value !== 'function')
    )
}
export const isObjectLike = (value: unknown) => {
    return typeof value === 'object' && value !== null
}

export const isNumber = (value: unknown) => {
    return typeof value === 'number' || (isObjectLike(value) && getType(value) === 'number')
}

export const { isNaN } = Number

export const isPromise = (value: any): value is Promise<any> => {
    if (!value) return false
    if (!value.then) return false
    if (!isFunction(value.then)) return false
    return true
}

export const isFloat = (value: any): value is number => {
    return isNumber(value) && value % 1 !== 0
}
export const isInt = (value: any): value is number => {
    return isNumber(value) && value % 1 === 0
}

export const isFunction = (value: any): value is (...args: any[]) => any => {
    return !!(value && value.constructor && value.call && value.apply)
}

export const isString = (value: any): value is string => {
    return typeof value === 'string' || value instanceof String
}

export const isEmpty = (value: any) => {
    if (value === true || value === false) return true
    if (value === null || value === undefined) return true
    if (isNumber(value)) return value === 0
    if (isDate(value)) return isNaN(value.getTime())
    if (isFunction(value)) return false
    if (isSymbol(value)) return false
    const { length } = value as any
    if (isNumber(length)) return length === 0
    const { size } = value as any
    if (isNumber(size)) return size === 0
    const keys = Object.keys(value).length
    return keys === 0
}

export const isEqual = <TType>(x: TType, y: TType): boolean => {
    if (Object.is(x, y)) return true
    if (x instanceof Date && y instanceof Date) {
        return x.getTime() === y.getTime()
    }
    if (x instanceof RegExp && y instanceof RegExp) {
        return x.toString() === y.toString()
    }
    if (typeof x !== 'object' || x === null || typeof y !== 'object' || y === null) {
        return false
    }
    const keysX = Reflect.ownKeys(x as unknown as object) as (keyof typeof x)[]
    const keysY = Reflect.ownKeys(y as unknown as object)
    if (keysX.length !== keysY.length) return false
    for (let i = 0; i < keysX.length; i++) {
        if (!Reflect.has(y as unknown as object, keysX[i])) return false
        if (!isEqual(x[keysX[i]], y[keysX[i]])) return false
    }
    return true
}
